package com.joinway.framework.admin.core.service.table;

import java.lang.reflect.Field

import javax.annotation.PostConstruct

import org.apache.commons.lang3.StringUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional

import com.joinway.framework.admin.core.bean.DataRecord
import com.joinway.framework.admin.core.bean.view.DataGridRecord
import com.joinway.framework.admin.core.bean.view.DataGridView
import com.joinway.framework.bean.constant.ErrorCodeConstants
import com.joinway.framework.bean.domain.constant.SqlConstants
import com.joinway.framework.bean.logging.annotation.InputLog
import com.joinway.framework.bean.view.View
import com.joinway.framework.core.plugin.PluginAdaptor
import com.joinway.framework.data.jdbc.bean.ConditionItem
import com.joinway.framework.data.jdbc.bean.Criteria
import com.joinway.framework.data.jdbc.bean.CriteriaItem
import com.joinway.framework.data.jdbc.bean.QueryCriteria
import com.joinway.framework.data.jdbc.bean.SaveCriteria
import com.joinway.framework.data.jdbc.bean.SelectClause
import com.joinway.framework.data.jdbc.bean.SelectResultSet
import com.joinway.framework.data.jdbc.bean.WhereClause
import com.joinway.framework.data.jdbc.repository.TableRepository
import com.joinway.framework.data.jdbc.service.SqlService
import com.joinway.framework.extension.groovy.DomainUtils
import com.joinway.framework.extension.groovy.GroovyUtils
import com.joinway.framework.extension.net.json.JsonConverter
import com.joinway.framework.extension.utils.DataUtils
import com.joinway.framework.extension.utils.DateTimeUtils

@Service("DefaultTableService")
public class TableService extends PluginAdaptor implements ITableService {

	private final static Logger log = LoggerFactory.getLogger(TableService.class);

	@Autowired protected TableRepository repository;

	protected SqlService service;
	
	@PostConstruct public void init(){
		service = new SqlService(repository)
	}
	
	@Override
	public String getTableName(){
		return "";
	}
	
	protected SelectResultSet findRecords(String table, String query, Integer page, Integer rows, Map<String, String[]> params) throws Exception{
		return null;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.joinway.appx.service.table.TableService#findRecord(java.lang.String, java.util.Map)
	 */
	@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
	@InputLog
	@Override
	public DataRecord find(String table, String query, Integer page, Integer rows, Map<String, String[]> params) throws Exception {
		List domains
		int count
		
		QueryCriteria criteria = buildQueryCriteria(table, query, page, rows);
		
		SelectResultSet result = findRecords(table, query, page, rows, params);
		if(result == null){
			SelectClause sc = buildSelectClause(criteria);
			result = service.selectRecords(sc);
			
//			domains = service.select(sc)
//			domains = service.select(criteria)
			
//			count = service.selectCount(sc);
//			count = service.selectCount(criteria);
//		}else{
			
//			String tableName = DomainUtils.convertToDBName(table)
//			String sql = getFindAllSql(tableName);
//			domains = repository.find(sql, page, rows)
//			def sum = repository.find("select count(*) total from $tableName")
//			count = sum ? sum[0].total : 0
		}
		
		domains = result.getRecords();
		count = result.getCount();
		
		domains.getProperties().each{
			GroovyUtils.removeASTFields(it)
		}

		DataRecord record = new DataRecord(domains:domains, count:count)
		return record
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.joinway.appx.service.table.TableService#saveTable(java.lang.String, java.util.Map)
	 */
	@Transactional(rollbackFor=Throwable.class)
	@InputLog
	@Override
	public View save(String table, String idField, Map<String, String> params) throws Exception {
		View view = new View();
		
		try{
			SaveCriteria criteria = new SaveCriteria()
			
			criteria.setTable(table)
			
			List<ConditionItem> items = []
			List<CriteriaItem> targets = []
			
			params.each {String fieldName, String value ->
				if(fieldName.equals(idField)){
					items << new ConditionItem("field":fieldName, "value":value)
				}else{
					targets << new CriteriaItem("field":fieldName, "value":value)
				}
			}
			
			criteria.items = items
			criteria.targets = targets
			
			service.persist(criteria)
		}catch(Exception e){
			view.setCode(ErrorCodeConstants.SAVE_FAILURE);
			view.setMessage(ErrorCodeConstants.SAVE_FAILURE_DESC);
			view.setError(e.getMessage());
			log.error("failed to save table " + table, e);
		}
		
		return view;
	}
	
	@Transactional(rollbackFor=Throwable.class)
	@InputLog
	@Override
	public int delete(String table, String idField, List<String> idValues) throws Exception {
		Criteria criteria = new Criteria()
		criteria.table = table
		
		List<ConditionItem> items = []
		idValues.each{
			if(it){
				items << new ConditionItem("field":idField, "value":it, "condition":SqlConstants.Condition.Or)
			}
		}
		criteria.items = items
		
		service.delete(criteria)
	}
	
	@Transactional(rollbackFor=Throwable.class)
	@InputLog
	@Override
	int delete(String table, String idField, List<String> idValues, String softField, String softFieldValue) throws Exception{
		SaveCriteria criteria = new SaveCriteria()
		criteria.table = table
		criteria.action = SqlConstants.PersistenceAction.Update
		
		List<CriteriaItem> targets = []
		targets << new CriteriaItem("field":softField, "value":softFieldValue)
		criteria.targets = targets
		
		List<ConditionItem> items = []
		idValues.each{
			if(it){
				items << new ConditionItem("field":idField, "value":it, "condition":SqlConstants.Condition.Or)
			}
		}
		criteria.items = items
		
		service.persist(criteria)
	}
	
	protected QueryCriteria buildQueryCriteria(String table, String query, Integer page, Integer rows){
		QueryCriteria criteria
		
		if(query){
			criteria = JsonConverter.jsonToObject(query, QueryCriteria)
			
			if(page){
				criteria.setPage(page)
			}
			if(rows){
				criteria.setRows(rows)
			}
			criteria.setTable(table)
			
			if(criteria.items){
				criteria.items.each {
					if(!it.operator){
						it.operator = SqlConstants.Operator.Like
					}
				}
			}
		}else{
			criteria = new QueryCriteria();
			
			criteria.setTable(table);
			
			if(page){
				criteria.setPage(page)
			}
			if(rows){
				criteria.setRows(rows)
			}
		}
		
		criteria
	}
	
	protected<T> T getDomainEntity(Map<String, String> params, Class<T> type) throws Exception {
		T domain = type.newInstance()
		
		Set<Field> fields = DataUtils.getValidFields(type)
		
		if(fields){
			fields.each {
				Field field = it
				
				String value = params.get(field.getName())
//				println value
				def v
				if(SqlConstants.RESERVED_VALUES.contains(value?.toLowerCase())){
					v = new Date()
				}else{
					if(value =~ /\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}/){
						v = DateTimeUtils.convertToDate(value)
					}else{
						v = DomainUtils.convertValue(field.getType(), value)
					}
				}
				
				domain."${field.name}" = v
			}
		}
		
		domain
	}
	
	protected DataGridView createDataGridView(List domains){
		return createDataGridView(domains, DataUtils.getSize(domains));
	}

	protected DataGridView createDataGridView(List domains, int count){
		DataGridView view = new DataGridView();
		DataGridRecord record = new DataGridRecord(domains, count);

		view.setRecord(record);
		
		return view;
	}

	protected String formatDateTime(def date){
		if(date instanceof String){
			String text = date.replaceAll("\\s+", "")
			text = StringUtils.replace(text, "-", "")
			text = StringUtils.replace(text, ":", "")
			text = StringUtils.replace(text, "/", "")
			text = StringUtils.replace(text, "\\", "")
			text = StringUtils.replace(text, ",", "")
			text
		}else{
			date
		}
	}
	
	@Deprecated
	protected String getFindAllSql(String tableName){
		return "select * from $tableName"
	}
	
	protected SelectClause buildSelectClause(QueryCriteria criteria){
		SelectClause sc = new SelectClause();
		
		String select = service.parseSelect(criteria);
		sc.setSelect(select);
		
		WhereClause wc = service.parseWhere(criteria);
		sc.setWhereClause(wc);
		
		String order = service.parseOrder(criteria);
		sc.setOrder(order);
		
		sc.setPage(criteria.getPage());
		sc.setRows(criteria.getRows());
		
		return sc;
	}
	
	protected List convertJsonArray(def text){
		List list = []
		
		try{
			if(text){
				List elements = text.trim().substring(1, text.length() - 3).trim().split('\\s*,\\s*')
				elements.each {
					if(it.trim()){
						list.add(it)
					}
				}
			}
		}catch(Exception e){
			log.error("failed to convert json array to list", e)
		}
		
		list
	}
	
}

